package com.workingpub.commons.jdbc.mapping;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;

import org.springframework.util.StringUtils;

public class JPARowMapping<T> extends AbstractRowMapping<T> {
	private Map<String, PropertyDescriptor> beanProperties;

	@Override
	protected T mapping(Class<T> resultType, ResultSet rs, int rowNum) throws Exception {
		if (!resultType.isAnnotationPresent(Entity.class)) {
			return null;
		}
		if (null == beanProperties) {
			BeanInfo beanInfo = Introspector.getBeanInfo(resultType, Object.class);
			PropertyDescriptor[] properties = beanInfo.getPropertyDescriptors();
			beanProperties = new HashMap<String, PropertyDescriptor>();
			for (PropertyDescriptor entry : properties) {
				beanProperties.put(entry.getName(), entry);
			}
		}
		T value = resultType.newInstance();
		resolveFields(resultType, rs, rowNum, value);
		resolveMethods(resultType, rs, rowNum, value);
		return value;
	}

	/**
	 * 按字段层注解解析
	 * 
	 * @param resultType
	 * @param rs
	 * @param rowNum
	 * @param target
	 * @throws Exception
	 */
	private void resolveFields(Class<?> resultType, ResultSet rs, int rowNum, Object target) throws Exception {
		if (resultType.isAnnotationPresent(Entity.class) || resultType.isAnnotationPresent(MappedSuperclass.class)) {
			Field[] fields = resultType.getDeclaredFields();
			for (Field item : fields) {
				String colName = null;
				if (item.isAnnotationPresent(Column.class)) {
					Column columnFlag = item.getAnnotation(Column.class);
					colName = StringUtils.hasText(columnFlag.name()) ? columnFlag.name() : item.getName();
				} else if (item.isAnnotationPresent(JoinColumn.class) && item.isAnnotationPresent(ManyToOne.class)) {
					JoinColumn columnFlag = item.getAnnotation(JoinColumn.class);
					colName = StringUtils.hasText(columnFlag.name()) ? columnFlag.name() : item.getName();
				} else if (item.isAnnotationPresent(Id.class)) {
					colName = "id";
				}
				if (StringUtils.hasText(colName)) {
					// item.set(target, convert(item.getType(), rs, colName));
					PropertyDescriptor property = beanProperties.get(item.getName());
					if (null != property) {
						property.getWriteMethod().invoke(target, convert(item.getType(), rs, colName));
					} else {
						logger.warn("Did not found the property : " + item);
					}
				}
			}
			resolveFields(resultType.getSuperclass(), rs, rowNum, target);
		}
	}

	/**
	 * 按方法层注解解析
	 * 
	 * @param resultType
	 * @param rs
	 * @param rowNum
	 * @param target
	 */
	private void resolveMethods(Class<T> resultType, ResultSet rs, int rowNum, Object target) throws Exception {
		for (Entry<String, PropertyDescriptor> entry : beanProperties.entrySet()) {
			Method readMethod = entry.getValue().getReadMethod();
			if (readMethod.isAnnotationPresent(Column.class)) {
				Column columnFlag = readMethod.getAnnotation(Column.class);
				String colName = StringUtils.hasText(columnFlag.name()) ? columnFlag.name()
						: entry.getValue().getName();
				Method writeMethod = entry.getValue().getWriteMethod();
				writeMethod.invoke(target, convert(readMethod.getReturnType(), rs, colName));
			}
		}
	}
}
